/*
 * 1. 抽象出落地的基类
 * 2. 派生子类（根据不同的落地方向进行派生）
 * 3，使用工厂模式创建和表示的分离
 *
 * */

#pragma once
#include "util.hpp"
#include <memory>
#include <cassert>
#include <sstream>
#include <fstream>

namespace log
{

    enum class TimeGap
    {
        GAP_SECOND,
        GAP_MINUTE,
        GAP_HOUR,
        GAP_DAY
    };

    class LogSink
    {
    public:
        using ptr = std::shared_ptr<LogSink>;

        LogSink() {}
        virtual ~LogSink() {}
        virtual void log(const char *data, size_t len) = 0;
    };

    class StdoutSink : public LogSink
    {
    public:
        void log(const char *data, size_t len) override
        {
            // 注意  const char* 是以\0为结束标志的，而日志输出不一定是字符串
            std::cout.write(data, len);
        }
    };

    // 落地方向： 指定文件
    class FileSink : public LogSink
    {
    public:
        FileSink(const std::string &pathname) : _pathname(pathname)
        {
            // 1.创建日志文件所在的路劲
            util::File::createDirector(util::File::path(pathname));
            // 2. 创建并打开日志文件
            _ofs.open(_pathname, std::ios::binary | std::ios::app);
            assert(_ofs.good());
        }

        void log(const char *data, size_t len) override
        {
            _ofs.write(data, len);
            assert(_ofs.good());
        }

    private:
        std::string _pathname;
        std::ofstream _ofs;
    };

    class RollByTimeSink : public LogSink
    {
    public:
        RollByTimeSink(const std::string &basename, TimeGap gap_type)
            : _basename(basename)
        {
            switch (gap_type)
            {
            case TimeGap::GAP_SECOND:
                _gap_size = 1;
                break;
            case TimeGap::GAP_MINUTE:
                _gap_size = 60;
                break;
            case TimeGap::GAP_HOUR:
                _gap_size = 3600;
                break;
            case TimeGap::GAP_DAY:
                _gap_size = 3600 * 24;
                break;
            }
            // 获取当前的时间段
            _cur_gap = _gap_size == 1 ? util::Data::getTime() : util::Data::getTime() % _gap_size;
            std::string filename = createNewFile();
            util::File::createDirector(util::File::path(filename));
            _ofs.open(filename, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }

        // 判断是不是当前的时间段，不是就切换文件
        void log(const char *data, size_t len)
        {
            time_t cur = util::Data::getTime();
            if ((cur % _gap_size) != _cur_gap)
            {
                // 关闭当前文件 然后切换文件
                _ofs.close();
                std::string filename = createNewFile();
                util::File::createDirector(util::File::path(filename));
                _ofs.open(filename, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
            }
            _ofs.write(data, len);
            assert(_ofs.good());
        }

    private:
        std::string createNewFile() // 进行大小判断，超过指定大小，就创建新文件
        {
            // 获取系统时间，来获取文件的扩展名字
            time_t t = util::Data::getTime();
            struct tm lt;
            // 这是时间是从1900年1月1日开始的
            localtime_r(&t, &lt);
            std::stringstream filename;
            filename << _basename;
            filename << lt.tm_year + 1900;
            filename << lt.tm_mon + 1;
            filename << lt.tm_wday;
            filename << lt.tm_hour;
            filename << lt.tm_min;
            filename << lt.tm_sec;
            filename << ".log";

            return filename.str();
        }

    private:
        std::string _basename;
        std::ofstream _ofs;
        size_t _cur_gap;
        size_t _gap_size;
    };

    class RollSizeSink : public LogSink
    {
    public:
        RollSizeSink(const std::string basename, size_t max_size) : _basename(basename), _max_size(max_size), _cur_size(0), _name_count(0)
        {
            std::string pathname = createNewFile();
            util::File::createDirector(util::File::path(pathname));
            _ofs.open(pathname, std::ios::binary | std::ios::app);
            assert(_ofs.good());
        }

        void log(const char *data, size_t len) override
        {
            if (_cur_size >= _max_size)
            {
                _ofs.close();
                // 切换文件，创建新文件名
                std::string pathname = createNewFile();
                _ofs.open(pathname, std::ios::binary | std::ios::app);
                _cur_size = 0;
            }
            _ofs.write(data, len);
            assert(_ofs.good());
            _cur_size += len;
        }

    private:
        std::string createNewFile() // 进行大小判断，超过指定大小，就创建新文件
        {
            // 获取系统时间，来获取文件的扩展名字
            time_t t = util::Data::getTime();
            struct tm lt;
            // 这是时间是从1900年1月1日开始的
            localtime_r(&t, &lt);
            std::stringstream filename;
            filename << _basename;
            filename << lt.tm_year + 1900;
            filename << lt.tm_mon + 1;
            filename << lt.tm_wday;
            filename << lt.tm_hour;
            filename << lt.tm_min;
            filename << lt.tm_sec;
            filename << "-";
            filename << _name_count++;
            filename << ".log";

            return filename.str();
        }

    private:
        size_t _name_count;
        // 追加基础的文件名
        std::string _basename; // ./logs/base-
        std::ofstream _ofs;
        size_t _max_size; // 记录最大大小，如果超过最大大小就切换
        size_t _cur_size; // 当前的文件大小
    };

    // 根据参数包来传递不同的参数来创建对象
    class SinkFactory
    {
    public:
        template <class SinkType, class... Args>
        static LogSink::ptr create(Args &&...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };

}
